//: [Previous](@previous)
// 拓展是不允许影响到原来类型的内存结构!所以比如不能添加存储属性
//: ### Double的扩展
//举例: 给Double添加扩展, 当前值转成对应的m
extension Double {
    var km: Double { self * 1_000.0 }
    var m: Double { self }
    var dm: Double { self / 10.0 }
    var cm: Double { self / 100.0 }
    var mm: Double { self / 1_000.0 }
}
var d = 100.0
d.km // 100,000(100km = 100,000m)
d.cm // 1(100cm = 1m)
//: ### Array的扩展
// 数组的扩展(*这个很实用,后面开发用得上), 判断数组越界, 就返回nil
var arr = [10, 20, 30]
//var arr1: Array[Int] = [10, 20, 30] 其实👆🏻写法是一样的
print(arr.startIndex) //0
print(arr.endIndex) //3

extension Array {
    subscript(nullable idx: Int) -> Element? {
        //nullable 是标记📌  Element 是Array系统代码里面的内容
        if (startIndex ..< endIndex).contains(idx) {
            return self[idx]
        }
        return nil
    }
}
//print(arr[4]) 4没有这个下标的元素,数组越界
print(arr[nullable: 4] as Any) //返回nil
print(arr[nullable: 4] ?? 0) //返回0
//:### Int的扩展
extension Int {
    
    //重复做一些事情
    func repeats( task: ()-> Void ) {
        for _ in 0 ..< self {
            task() //尾随闭包
        }
    }
    
    // 求平方
    mutating func square() -> Int {
        self = self * self
        return self
    }
    
    // 定义嵌套类型(在一个类型里面定义另外一个类型)(在Int类型里面定义了Kind类型) 枚举
    enum Kind { case negative, zero, positive}
    //negative:负数,zero:0,positive:正数
    var kind: Kind { //只读的计算属性
        switch self {
        case 0:
            return .zero
        case let x where x > 0:
            return .positive
        default:
            return .negative
        }
    }
    
    //下标
    subscript(digitIndex: Int) -> Int {//传入下标,活得第几位数字,比如传入0就是返回个位数,传入1就是返回十位数...
        var decimalBase = 1
        for _ in 0..<digitIndex {
            decimalBase *= 10
        }
        return ( self / decimalBase) % 10
    }
}

3.repeats { //重复执行下面代码 3次
    print(1)
}
//1
//1
//1

var sq1 = 10
sq1.square()

print(sq1.kind) //positive
print(0.kind) //zero

var su1 = 12345
print(su1[0]) //个位数:5
print(su1[3]) //千位数:5
print(su1[4]) //万位数:5

//: ### 协议
class Person1 {
    var age: Int
    var name: String
    init(age: Int, name: String) {//类本身的指定初始化器
        self.age = age
        self.name = name
    }
}

//给类扩展了Equatable这个协议
extension Person1 : Equatable {
    static func == (lhs: Person1, rhs: Person1) -> Bool {
        lhs.age == rhs.age && lhs.name == rhs.name
    }
    
    convenience init() {//便捷初始化器可以在扩展里面使用, 指定初始化器不行
        self.init(age: 0, name: "")
    }
}

var pClass1 = Person1(age: 10, name: "Jack")
var pClass2 = Person1(age: 10, name: "Sky")
var pClass3 = Person1() //
print(pClass1 == pClass2) //false

// 给结构体拓展,当你想自定义结构体的初始化器,最好是在拓展去写,这样子就可以保留系统给结构体默认的几个初始化器(这个知识点开发肯定有用)
struct ponitStruct {
    var x = 0, y = 0
}

extension ponitStruct {
    init(_ point: ponitStruct) {//结构体是不分指定/便捷初始化器的,所以可以在拓展使用
        self.init(x: point.x, y: point.y)
    }
}
var ps1 = ponitStruct()
var ps2 = ponitStruct(ps1)
var ps3 = ponitStruct(x: 10, y: 20)

/*
 1. 如果希望自定义初始化器的同时, 编译器也能生成默认初始化器, 那么可以在拓展中编写自定义初始化器
 2. 类遵守协议(有初始化器)实现的required初始化器, 不能写在拓展中
 */
protocol Runnable { //这个协议,有初始化器
    init(age: Int)
}
class Person : Runnable { //类遵守了有初始化器的协议,就必须实现这个初始化器,而且必须在类本身实现,不能放到拓展去实现
    required init(age: Int) {
        
    }
}
//extension Person {//报错
//    required init(age: Int) {
//        
//    }
//}

/*
 如果一个类型已经实现了一个协议的所有要求, 但是还没有声明它遵守了这个协议
 可以通过拓展来让它遵守这个协议, 这个时候实现一般就直接空就行
 */
protocol Test1Protocol {
    func test()
}
class Test1Class {
    func test() {}
}
extension Test1Class : Test1Protocol {}

// 举个例子: 判断一个整数是否为奇数? (整数 : 是包括Int,Int8,Int16...)\
//方法一, 直接写方法
/*
 Odd(中文:奇数)
 BinaryInteger:这是一个系统自带的协议, 所有Int8 16 64类型都遵守, 也就是说所有的整数都遵守, 所以遵守这个协议的就可以表示为这个是整数
 i % 2 != 0 : 除以2有余数,就是奇数,返回true
 */
func isOdd<T: BinaryInteger>(_ i: T) -> Bool {
    i % 2 != 0
}
var od1: Int = 10
var od2: Int8 = 20
isOdd(od1) //false
isOdd(od2) //false

//方法2,毕竟这个是对整数的一个需求,其实可以直接对整数去写拓展呀,怎么样才确定是整数?满足BinaryInteger的就是了,所以直接对这个协议拓展就行[*这种思维在开发中肯定用得上,要学习]
extension BinaryInteger {
    func isOdd() -> Bool {
        self % 2 != 0
    }
}
var od3: Int = 10
var od4: Int8 = 20
od3.isOdd() //false
od4.isOdd() //false

/*
 关于协议的拓展
 1. 拓展可以给协议提供默认的实现, 所以达到了[可选协议]的效果
 2. 拓展可以给协议扩充在协议中从未声明过的方法
 */
protocol testPtotocol1 {
    func test1()
}
extension testPtotocol1 {
    func test1() {// 1. 协议的方法可以直接在拓展里面实现,这样子遵守协议的类型就不再必须实现这个方法了
        print(" extension testPtotocol1 test1 ")
    }
    func test2() {// 2. 在协议本身没有声明过的方法, 也可以在拓展里面直接创建并实现, 这样子遵守这个协议的类型也可以直接使用这个协议一开始并没有的方法
        print(" extension testPtotocol1 test2 ")
    }
}
class testClass2 : testPtotocol1 {}
var tc2 = testClass2()
tc2.test1()//extension testPtotocol1 test1
tc2.test2()//extension testPtotocol1 test2

class testClass3 : testPtotocol1 {//当类型都实现了协议里面的方法
    func test1() {
        print("testClass3 test1")
    }
    func test2() {
        print("testClass3 test2")
    }
}
var tc3 = testClass3()
tc3.test1() //testClass3 test1
tc3.test2() //testClass3 test2
//👆🏻 因为testClass3类型实现了方法,tc3属于testClass3类型,所以会直接执行类里面的方法,不会再访问到协议

var tc4 : testPtotocol1 = testClass3()
tc4.test1() //testClass3 test1
tc4.test2() //extension testPtotocol1 test2 👈🏻注意📢
//👆🏻 因为testPtotocol1并没有声明test2方法, tc4属于了testPtotocol1, 所以系统会判断tc4也没有声明test2, 所以判断testClass3()这个类也是没有test2的了(直接被忽略了), 直接跳到testPtotocol1的拓展里面找了, 找到了有所以就直接实现了协议里面的方法
//: ### 泛型
class Stack<E> {
    var elements = [E]()
    func push(_ element: E) {
        elements.append(element)
    }
    func pop() -> E {
        elements.removeLast()
    }
    func size() -> Int {
        elements.count
    }
}
// 拓展中依然可以使用原类型中的泛型
extension Stack {
    func top() -> E {
        elements.last!
    }
}
// 判断符合条件才实现的拓展
extension Stack : Equatable where E : Equatable {
    // 给Stack拓展遵守Equatable协议, 但是不是直接生效的, 是要(where)泛型类型符合Equatable协议的情况下才会生效.
    static func ==(left: Stack, right: Stack) -> Bool {
        left.elements == right.elements
    }
}












//: [Next](@next)
